home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 16 / AMIGAplus Sonderheft 16 (1998)(ICP)(DE)[!].iso / pd / anwendungen / ispell-3.1.18src / tree.c < prev    next >
C/C++ Source or Header  |  1995-09-13  |  22KB  |  775 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: tree.c,v 1.56 1995/01/08 23:23:49 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * tree.c - a hash style dictionary for user's personal words
  8.  *
  9.  * Pace Willisson, 1983
  10.  * Hash support added by Geoff Kuenning, 1987
  11.  *
  12.  * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA
  13.  * All rights reserved.
  14.  *
  15.  * Redistribution and use in source and binary forms, with or without
  16.  * modification, are permitted provided that the following conditions
  17.  * are met:
  18.  *
  19.  * 1. Redistributions of source code must retain the above copyright
  20.  *    notice, this list of conditions and the following disclaimer.
  21.  * 2. Redistributions in binary form must reproduce the above copyright
  22.  *    notice, this list of conditions and the following disclaimer in the
  23.  *    documentation and/or other materials provided with the distribution.
  24.  * 3. All modifications to the source code must be clearly marked as
  25.  *    such.  Binary redistributions based on modified source code
  26.  *    must be clearly marked as modified versions in the documentation
  27.  *    and/or other materials provided with the distribution.
  28.  * 4. All advertising materials mentioning features or use of this software
  29.  *    must display the following acknowledgment:
  30.  *      This product includes software developed by Geoff Kuenning and
  31.  *      other unpaid contributors.
  32.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  33.  *    products derived from this software without specific prior
  34.  *    written permission.
  35.  *
  36.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  37.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  38.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  39.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  40.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  41.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  42.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  44.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  45.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46.  * SUCH DAMAGE.
  47.  */
  48.  
  49. /*
  50.  * $Log: tree.c,v $
  51.  * Revision 1.56  1995/01/08  23:23:49  geoff
  52.  * Support PDICTHOME for DOS purposes.
  53.  *
  54.  * Revision 1.55  1994/10/25  05:46:27  geoff
  55.  * Fix a comment that looked to some compilers like it might be nested.
  56.  *
  57.  * Revision 1.54  1994/01/25  07:12:15  geoff
  58.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  59.  *
  60.  */
  61.  
  62. #include <ctype.h>
  63. #include <errno.h>
  64. #include "config.h"
  65. #include "ispell.h"
  66. #include "proto.h"
  67. #include "msgs.h"
  68.  
  69. void        treeinit P ((char * p, char * LibDict));
  70. static FILE *    trydict P ((char * dictname, char * home,
  71.           char * prefix, char * suffix));
  72. static void    treeload P ((FILE * dictf));
  73. void        treeinsert P ((char * word, int wordlen, int keep));
  74. static struct dent * tinsert P ((struct dent * proto));
  75. struct dent *    treelookup P ((ichar_t * word));
  76. #if SORTPERSONAL != 0
  77. static int    pdictcmp P ((struct dent ** enta, struct dent **entb));
  78. #endif /* SORTPERSONAL != 0 */
  79. void        treeoutput P ((void));
  80. VOID *        mymalloc P ((unsigned int size));
  81. void        myfree P ((VOID * ptr));
  82. #ifdef REGEX_LOOKUP
  83. char *        do_regex_lookup P ((char * expr, int whence));
  84. #endif /* REGEX_LOOKUP */
  85.  
  86. static int        cantexpand = 0;    /* NZ if an expansion fails */
  87. static struct dent *    pershtab;    /* Aux hash table for personal dict */
  88. static int        pershsize = 0;    /* Space available in aux hash table */
  89. static int        hcount = 0;    /* Number of items in hash table */
  90.  
  91. /*
  92.  * Hash table sizes.  Prime is probably a good idea, though in truth I
  93.  * whipped the algorithm up on the spot rather than looking it up, so
  94.  * who knows what's really best?  If we overflow the table, we just
  95.  * use a double-and-add-1 algorithm.
  96.  */
  97. static int goodsizes[] =
  98.     {
  99.     53, 223, 907, 3631
  100.     };
  101.  
  102. static char        personaldict[MAXPATHLEN];
  103. static FILE *        dictf;
  104. static            newwords = 0;
  105.  
  106. void treeinit (p, LibDict)
  107.     char *        p;        /* Value specified in -p switch */
  108.     char *        LibDict;    /* Root of default dict name */
  109.     {
  110.     int            abspath;    /* NZ if p is abs path name */
  111. #ifndef AMIGA
  112.     char *        h;        /* Home directory name */
  113. #endif
  114.     char        seconddict[MAXPATHLEN]; /* Name of secondary dict */
  115.     FILE *        secondf;    /* Access to second dict file */
  116.  
  117. #if 0
  118.     printf("Entering treeinit(); '%s'\n", personaldict);
  119. #endif
  120.  
  121.     /*
  122.     ** If -p was not specified, try to get a default name from the
  123.     ** environment.  After this point, if p is null, the the value in
  124.     ** personaldict is the only possible name for the personal dictionary.
  125.     ** If p is non-null, then there is a possibility that we should
  126.     ** prepend HOME to get the correct dictionary name.
  127.     */
  128.     if (p == NULL)
  129.     p = getenv (PDICTVAR);
  130.     /*
  131.     ** if p exists and begins with '/' we don't really need HOME,
  132.     ** but it's not very likely that HOME isn't set anyway (on non-DOS
  133.     ** systems).
  134.     */
  135. #ifndef AMIGA
  136.     /*
  137.      * This code is of no use on the Amiga - 'h' is not used
  138.      * below.  The only thing that happens is getenv() fails
  139.      * -  always on my system  -  and the function exits and
  140.      * personal dictionaries can't be used.
  141.      *
  142.      */
  143.     if ((h = getenv (HOME)) == NULL)
  144.     {
  145. #ifdef PDICTHOME
  146.     h = PDICTHOME;
  147. #else /* PDICTHOME */
  148. #if 0
  149.     printf("Exiting treeinit() due to HOME %s == %s\n", HOME, h);
  150.     (void)getc(stdin);
  151. #endif
  152.     return;
  153. #endif /* PDICTHOME */
  154.     }
  155. #endif /* AMIGA */
  156.  
  157.     if (p == NULL)
  158.     {
  159.     /*
  160.      * No -p and no PDICTVAR.  We will use LibDict and DEFPAFF to
  161.      * figure out the name of the personal dictionary and where it
  162.      * is.  The rules are as follows:
  163.      *
  164.      * (1) If there is a local dictionary and a HOME dictionary,
  165.      *     both are loaded, but changes are saved in the local one.
  166.      *     The dictionary to save changes in is named "personaldict".
  167.      * (2) Dictionaries named after the affix file take precedence
  168.      *     over dictionaries with the default suffix (DEFPAFF).
  169.      * (3) Dictionaries named with the new default names
  170.      *     (DEFPDICT/DEFPAFF) take precedence over the old ones
  171.      *     (OLDPDICT/OLDPAFF).
  172.      * (4) Dictionaries aren't combined unless they follow the same
  173.      *     naming scheme.
  174.      * (5) If no dictionary can be found, a new one is created in
  175.      *     the home directory, named after DEFPDICT and the affix
  176.      *     file.
  177.      */
  178. #ifdef AMIGA    /* Personal dicts are placed with the primary dicts */
  179.     dictf = trydict (personaldict, LIBDIR, DEFPDICT, LibDict);
  180.     if (personaldict[0] == '\0')
  181.       sprintf (personaldict, "%s/%s%s", LIBDIR, DEFPDICT, LibDict);
  182. #else
  183.     dictf = trydict (personaldict, (char *) NULL, DEFPDICT, LibDict);
  184.     secondf = trydict (seconddict, h, DEFPDICT, LibDict);
  185.     if (dictf == NULL  &&  secondf == NULL)
  186.         {
  187.         dictf = trydict (personaldict, (char *) NULL, DEFPDICT, DEFPAFF);
  188.         secondf = trydict (seconddict, h, DEFPDICT, DEFPAFF);
  189.         }
  190.     if (dictf == NULL  &&  secondf == NULL)
  191.         {
  192.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, LibDict);
  193.         secondf = trydict (seconddict, h, OLDPDICT, LibDict);
  194.         }
  195.     if (dictf == NULL  &&  secondf == NULL)
  196.         {
  197.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, OLDPAFF);
  198.         secondf = trydict (seconddict, h, OLDPDICT, OLDPAFF);
  199.         }
  200.     if (personaldict[0] == '\0')
  201.         {
  202.         if (seconddict[0] != '\0')
  203.         (void) strcpy (personaldict, seconddict);
  204.         else
  205.         (void) sprintf (personaldict, "%s/%s%s", h, DEFPDICT, LibDict);
  206.         }
  207. #endif
  208.     if (dictf != NULL)
  209.         {
  210.         treeload (dictf);
  211.         (void) fclose (dictf);
  212.         }
  213. #ifndef AMIGA                                /* Only one dict */
  214.     if (secondf != NULL)
  215.         {
  216.         treeload (secondf);
  217.         (void) fclose (secondf);
  218.         }
  219. #endif
  220.     }
  221.     else
  222.     {
  223.     /*
  224.     ** Figure out if p is an absolute path name.  Note that beginning
  225.     ** with "./" and "../" is considered an absolute path, since this
  226.     ** still means we can't prepend HOME.
  227.     */
  228. #ifndef AMIGA                                /* Always absolute path on Amiga */
  229.     abspath = (*p == '/'  ||  strncmp (p, "./", 2) == 0
  230.       ||  strncmp (p, "../", 3) == 0);
  231.     if (abspath)
  232.         {
  233. #endif
  234.         (void) strcpy (personaldict, p);
  235.         if ((dictf = fopen (personaldict, "r")) != NULL)
  236.         {
  237.         treeload (dictf);
  238.         (void) fclose (dictf);
  239.         }
  240. #ifndef AMIGA
  241.         }
  242.     else
  243.         {
  244.         /*
  245.         ** The user gave us a relative pathname.  We will try it
  246.         ** locally, and if that doesn't work, we'll try the home
  247.         ** directory.  If neither exists, it will be created in
  248.         ** the home directory if words are added.
  249.         */
  250.         (void) strcpy (personaldict, p);
  251.         if ((dictf = fopen (personaldict, "r")) != NULL)
  252.         {
  253.         treeload (dictf);
  254.         (void) fclose (dictf);
  255.         }
  256.         else if (!abspath)
  257.         {
  258.         /* Try the home */
  259.         (void) sprintf (personaldict, "%s/%s", h, p);
  260.         if ((dictf = fopen (personaldict, "r")) != NULL)
  261.             {
  262.             treeload (dictf);
  263.             (void) fclose (dictf);
  264.             }
  265.         }
  266.         /*
  267.          * If dictf is null, we couldn't open the dictionary
  268.          * specified in the -p switch.  Complain.
  269.          */
  270.         if (dictf == NULL)
  271.         {
  272.         (void) fprintf (stderr, CANT_OPEN, p);
  273.         perror ("");
  274.         return;
  275.         }
  276.         }
  277. #endif
  278.     }
  279.  
  280.     if (!lflag  &&  !aflag
  281.       &&  access (personaldict, 2) < 0  &&  errno != ENOENT)
  282.     {
  283.     (void) fprintf (stderr, TREE_C_CANT_UPDATE, personaldict);
  284.     (void) sleep ((unsigned) 2);
  285.     }
  286.  
  287. #if 0
  288.     printf("Exiting treeinit(); '%s'\n", personaldict);
  289.     (void)getc(stdin);
  290. #endif
  291.  
  292.     }
  293.  
  294. /*
  295.  * Try to open a dictionary.  As a side effect, leaves the dictionary
  296.  * name in "filename" if one is found, and leaves a null string there
  297.  * otherwise.
  298.  */
  299. static FILE * trydict (filename, home, prefix, suffix)
  300.     char *        filename;    /* Where to store the file name */
  301.     char *        home;        /* Home directory */
  302.     char *        prefix;        /* Prefix for dictionary */
  303.     char *        suffix;        /* Suffix for dictionary */
  304.     {
  305.     FILE *        dictf;        /* Access to dictionary file */
  306.  
  307.     if (home == NULL)
  308.     (void) sprintf (filename, "%s%s", prefix, suffix);
  309.     else
  310.     (void) sprintf (filename, "%s/%s%s", home, prefix, suffix);
  311.     dictf = fopen (filename, "r");
  312.     if (dictf == NULL)
  313.     filename[0] = '\0';
  314.     return dictf;
  315.     }
  316.  
  317. static void treeload (loadfile)
  318.     register FILE *    loadfile;    /* File to load words from */
  319.     {
  320.     char        buf[BUFSIZ];    /* Buffer for reading pers dict */
  321.  
  322.     while (fgets (buf, sizeof buf, loadfile) != NULL)
  323.     treeinsert (buf, sizeof buf, 1);
  324.     newwords = 0;
  325.     }
  326.  
  327. void treeinsert (word, wordlen, keep)
  328.     char *        word;    /* Word to insert - must be canonical */
  329.     int            wordlen; /* Length of the word buffer */
  330.     int            keep;
  331.     {
  332.     register int    i;
  333.     struct dent        wordent;
  334.     register struct dent * dp;
  335.     struct dent *    olddp;
  336. #ifndef NO_CAPITALIZATION_SUPPORT
  337.     struct dent *    newdp;
  338. #endif
  339.     struct dent *    oldhtab;
  340.     int            oldhsize;
  341.     ichar_t        nword[INPUTWORDLEN + MAXAFFIXLEN];
  342. #ifndef NO_CAPITALIZATION_SUPPORT
  343.     int            isvariant;
  344. #endif
  345.  
  346.     /*
  347.      * Expand hash table when it is MAXPCT % full.
  348.      */
  349.     if (!cantexpand  &&  (hcount * 100) / MAXPCT >= pershsize)
  350.     {
  351.     oldhsize = pershsize;
  352.     oldhtab = pershtab;
  353.     for (i = 0;  i < sizeof goodsizes / sizeof (goodsizes[0]);  i++)
  354.         {
  355.         if (goodsizes[i] > pershsize)
  356.         break;
  357.         }
  358.     if (i >= sizeof goodsizes / sizeof goodsizes[0])
  359.         pershsize += pershsize + 1;
  360.     else
  361.         pershsize = goodsizes[i];
  362.     pershtab =
  363.       (struct dent *) calloc ((unsigned) pershsize, sizeof (struct dent));
  364.     if (pershtab == NULL)
  365.         {
  366.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  367.         /*
  368.          * Try to continue anyway, since our overflow
  369.          * algorithm can handle an overfull (100%+) table,
  370.          * and the malloc very likely failed because we
  371.          * already have such a huge table, so small mallocs
  372.          * for overflow entries will still work.
  373.          */
  374.         if (oldhtab == NULL)
  375.         exit (1);        /* No old table, can't go on */
  376.         (void) fprintf (stderr, TREE_C_TRY_ANYWAY);
  377.         cantexpand = 1;        /* Suppress further messages */
  378.         pershsize = oldhsize;    /* Put things back */
  379.         pershtab = oldhtab;        /* ... */
  380.         newwords = 1;        /* And pretend it worked */
  381.         }
  382.     else
  383.         {
  384.         /*
  385.          * Re-insert old entries into new table
  386.          */
  387.         for (i = 0;  i < oldhsize;  i++)
  388.         {
  389.         dp = &oldhtab[i];
  390.         if (dp->flagfield & USED)
  391.             {
  392. #ifdef NO_CAPITALIZATION_SUPPORT
  393.             (void) tinsert (dp);
  394. #else
  395.             newdp = tinsert (dp);
  396.             isvariant = (dp->flagfield & MOREVARIANTS);
  397. #endif
  398.             dp = dp->next;
  399. #ifdef NO_CAPITALIZATION_SUPPORT
  400.             while (dp != NULL)
  401.             {
  402.             (void) tinsert (dp);
  403.             olddp = dp;
  404.             dp = dp->next;
  405.             free ((char *) olddp);
  406.             }
  407. #else
  408.             while (dp != NULL)
  409.             {
  410.             if (isvariant)
  411.                 {
  412.                 isvariant = dp->flagfield & MOREVARIANTS;
  413.                 olddp = newdp->next;
  414.                 newdp->next = dp;
  415.                 newdp = dp;
  416.                 dp = dp->next;
  417.                 newdp->next = olddp;
  418.                 }
  419.             else
  420.                 {
  421.                 isvariant = dp->flagfield & MOREVARIANTS;
  422.                 newdp = tinsert (dp);
  423.                 olddp = dp;
  424.                 dp = dp->next;
  425.                 free ((char *) olddp);
  426.                 }
  427.             }
  428. #endif
  429.             }
  430.         }
  431.         if (oldhtab != NULL)
  432.         free ((char *) oldhtab);
  433.         }
  434.     }
  435.  
  436.     /*
  437.     ** We're ready to do the insertion.  Start by creating a sample
  438.     ** entry for the word.
  439.     */
  440.     if (makedent (word, wordlen, &wordent) < 0)
  441.     return;            /* Word must be too big or something */
  442.     if (keep)
  443.     wordent.flagfield |= KEEP;
  444.     /*
  445.     ** Now see if word or a variant is already in the table.  We use the
  446.     ** capitalized version so we'll find the header, if any.
  447.     **/
  448.     (void) strtoichar (nword, word, sizeof nword, 1);
  449.     upcase (nword);
  450.     if ((dp = lookup (nword, 1)) != NULL)
  451.     {
  452.     /* It exists.  Combine caps and set the keep flag. */
  453.     if (combinecaps (dp, &wordent) < 0)
  454.         {
  455.         free (wordent.word);
  456.         return;
  457.         }
  458.     }
  459.     else
  460.     {
  461.     /* It's new. Insert the word. */
  462.     dp = tinsert (&wordent);
  463. #ifndef NO_CAPITALIZATION_SUPPORT
  464.     if (captype (dp->flagfield) == FOLLOWCASE)
  465.        (void) addvheader (dp);
  466. #endif
  467.     }
  468.     newwords |= keep;
  469.     }
  470.  
  471. static struct dent * tinsert (proto)
  472.     struct dent *    proto;        /* Prototype entry to copy */
  473.     {
  474.     ichar_t        iword[INPUTWORDLEN + MAXAFFIXLEN];
  475.     register int    hcode;
  476.     register struct dent * hp;        /* Next trial entry in hash table */
  477.     register struct dent * php;        /* Prev. value of hp, for chaining */
  478.  
  479.     if (strtoichar (iword, proto->word, sizeof iword, 1))
  480.     (void) fprintf (stderr, WORD_TOO_LONG (proto->word));
  481. #ifdef NO_CAPITALIZATION_SUPPORT
  482.     upcase (iword);
  483. #endif
  484.     hcode = hash (iword, pershsize);
  485.     php = NULL;
  486.     hp = &pershtab[hcode];
  487.     if (hp->flagfield & USED)
  488.     {
  489.     while (hp != NULL)
  490.         {
  491.         php = hp;
  492.         hp = hp->next;
  493.         }
  494.     hp = (struct dent *) calloc (1, sizeof (struct dent));
  495.     if (hp == NULL)
  496.         {
  497.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  498.         exit (1);
  499.         }
  500.     }
  501.     *hp = *proto;
  502.     if (php != NULL)
  503.     php->next = hp;
  504.     hp->next = NULL;
  505.     return hp;
  506.     }
  507.  
  508. struct dent * treelookup (word)
  509.     register ichar_t *    word;
  510.     {
  511.     register int    hcode;
  512.     register struct dent * hp;
  513.     char        chword[INPUTWORDLEN + MAXAFFIXLEN];
  514.  
  515.     if (pershsize <= 0)
  516.     return NULL;
  517.     (void) ichartostr (chword, word, sizeof chword, 1);
  518.     hcode = hash (word, pershsize);
  519.     hp = &pershtab[hcode];
  520.     while (hp != NULL  &&  (hp->flagfield & USED))
  521.     {
  522.     if (strcmp (chword, hp->word) == 0)
  523.         break;
  524. #ifndef NO_CAPITALIZATION_SUPPORT
  525.     while (hp->flagfield & MOREVARIANTS)
  526.         hp = hp->next;
  527. #endif
  528.     hp = hp->next;
  529.     }
  530.     if (hp != NULL  &&  (hp->flagfield & USED))
  531.     return hp;
  532.     else
  533.     return NULL;
  534.     }
  535.  
  536. #if SORTPERSONAL != 0
  537. /* Comparison routine for sorting the personal dictionary with qsort */
  538. static int pdictcmp (enta, entb)
  539.     struct dent **    enta;
  540.     struct dent **    entb;
  541.     {
  542.  
  543.     /* The parentheses around *enta and *entb below are NECESSARY!
  544.     ** Otherwise the compiler reads it as *(enta->word), or
  545.     ** enta->word[0], which is illegal (but pcc takes it and
  546.     ** produces wrong code).
  547.     **/
  548.     return casecmp ((*enta)->word, (*entb)->word, 1);
  549.     }
  550. #endif
  551.  
  552. void treeoutput ()
  553.     {
  554.     register struct dent *    cent;    /* Current entry */
  555.     register struct dent *    lent;    /* Linked entry */
  556. #if SORTPERSONAL != 0
  557.     int                pdictsize; /* Number of entries to write */
  558.     struct dent **        sortlist; /* List of entries to be sorted */
  559.     register struct dent **    sortptr; /* Handy pointer into sortlist */
  560. #endif
  561.     register struct dent *    ehtab;    /* End of pershtab, for fast looping */
  562.  
  563.     if (newwords == 0)
  564.     return;
  565.  
  566.     printf("Saving to '%s'", personaldict);
  567.     fflush(stdout);
  568.  
  569.     if ((dictf = fopen (personaldict, "w")) == NULL)
  570.     {
  571.     (void) fprintf (stderr, CANT_CREATE, personaldict);
  572.     return;
  573.     }
  574.  
  575. #if SORTPERSONAL != 0
  576.     /*
  577.     ** If we are going to sort the personal dictionary, we must know
  578.     ** how many items are going to be sorted.
  579.     */
  580.     pdictsize = 0;
  581.     if (hcount >= SORTPERSONAL)
  582.     sortlist = NULL;
  583.     else
  584.     {
  585.     for (cent = pershtab, ehtab = pershtab + pershsize;
  586.       cent < ehtab;
  587.       cent++)
  588.         {
  589.         for (lent = cent;  lent != NULL;  lent = lent->next)
  590.         {
  591.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  592.             pdictsize++;
  593. #ifndef NO_CAPITALIZATION_SUPPORT
  594.         while (lent->flagfield & MOREVARIANTS)
  595.           lent = lent->next;
  596. #endif
  597.         }
  598.         }
  599.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  600.       cent < ehtab;
  601.       cent++)
  602.         {
  603.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  604.         {
  605.         /*
  606.         ** We only want to count variant headers
  607.         ** and standalone entries.  These happen
  608.         ** to share the characteristics in the
  609.         ** test below.  This test will appear
  610.         ** several more times in this routine.
  611.         */
  612. #ifndef NO_CAPITALIZATION_SUPPORT
  613.         if (captype (cent->flagfield) != FOLLOWCASE
  614.           &&  cent->word != NULL)
  615. #endif
  616.             pdictsize++;
  617.         }
  618.         }
  619.     sortlist = (struct dent **) malloc (pdictsize * sizeof (struct dent));
  620.     }
  621.     if (sortlist == NULL)
  622.     {
  623. #endif
  624.     for (cent = pershtab, ehtab = pershtab + pershsize;
  625.       cent < ehtab;
  626.       cent++)
  627.         {
  628.         for (lent = cent;  lent != NULL;  lent = lent->next)
  629.         {
  630.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  631.             {
  632.             toutent (dictf, lent, 1);
  633. #ifndef NO_CAPITALIZATION_SUPPORT
  634.             while (lent->flagfield & MOREVARIANTS)
  635.             lent = lent->next;
  636. #endif
  637.             }
  638.         }
  639.         }
  640.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  641.       cent < ehtab;
  642.       cent++)
  643.         {
  644.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  645.         {
  646. #ifndef NO_CAPITALIZATION_SUPPORT
  647.         if (captype (cent->flagfield) != FOLLOWCASE
  648.           &&  cent->word != NULL)
  649. #endif
  650.             toutent (dictf, cent, 1);
  651.         }
  652.         }
  653. #if SORTPERSONAL != 0
  654.     return;
  655.     }
  656.     /*
  657.     ** Produce dictionary in sorted order.  We used to do this
  658.     ** destructively, but that turns out to fail because in some modes
  659.     ** the dictionary is written more than once.  So we build an
  660.     ** auxiliary pointer table (in sortlist) and sort that.  This
  661.     ** is faster anyway, though it uses more memory. 
  662.     */
  663.     sortptr = sortlist;
  664.     for (cent = pershtab, ehtab = pershtab + pershsize;  cent < ehtab;  cent++)
  665.     {
  666.     for (lent = cent;  lent != NULL;  lent = lent->next)
  667.         {
  668.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  669.         {
  670.         *sortptr++ = lent;
  671. #ifndef NO_CAPITALIZATION_SUPPORT
  672.         while (lent->flagfield & MOREVARIANTS)
  673.             lent = lent->next;
  674. #endif
  675.         }
  676.         }
  677.     }
  678.     for (cent = hashtbl, ehtab = hashtbl + hashsize;  cent < ehtab;  cent++)
  679.     {
  680.     if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  681.         {
  682. #ifndef NO_CAPITALIZATION_SUPPORT
  683.         if (captype (cent->flagfield) != FOLLOWCASE
  684.           &&  cent->word != NULL)
  685. #endif
  686.         *sortptr++ = cent;
  687.         }
  688.     }
  689.     /* Sort the list */
  690.     qsort ((char *) sortlist, (unsigned) pdictsize,
  691.       sizeof (sortlist[0]),
  692.       (int (*) P ((const void *, const void *))) pdictcmp);
  693.     /* Write it out */
  694.     for (sortptr = sortlist;  --pdictsize >= 0;  )
  695.     toutent (dictf, *sortptr++, 1);
  696.     free ((char *) sortlist);
  697. #endif
  698.  
  699.     newwords = 0;
  700.  
  701.     (void) fclose (dictf);
  702.     }
  703.  
  704. VOID * mymalloc (size)
  705.     unsigned int    size;
  706.     {
  707.  
  708.     return malloc ((unsigned) size);
  709.     }
  710.  
  711. void myfree (ptr)
  712.     VOID * ptr;
  713.     {
  714.     if (hashstrings != NULL  &&  (char *) ptr >= hashstrings
  715.       &&  (char *) ptr <= hashstrings + hashheader.stringsize)
  716.     return;            /* Can't free stuff in hashstrings */
  717.     free (ptr);
  718.     }
  719.  
  720. #ifdef REGEX_LOOKUP
  721.  
  722. /* check the hashed dictionary for words matching the regex. return the */
  723. /* a matching string if found else return NULL */
  724. char * do_regex_lookup (expr, whence)
  725.     char *    expr;    /* regular expression to use in the match   */
  726.     int        whence;    /* 0 = start at the beg with new regx, else */
  727.             /* continue from cur point w/ old regex     */
  728.     {
  729.     static struct dent *    curent;
  730.     static int            curindex;
  731.     static struct dent *    curpersent;
  732.     static int            curpersindex;
  733.     static char *        cmp_expr;
  734.     char            dummy[INPUTWORDLEN + MAXAFFIXLEN];
  735.     ichar_t *            is;
  736.  
  737.     if (whence == 0)
  738.     {
  739.     is = strtosichar (expr, 0);
  740.     upcase (is);
  741.     expr = ichartosstr (is, 1);
  742.         cmp_expr = REGCMP (expr);
  743.         curent = hashtbl;
  744.         curindex = 0;
  745.         curpersent = pershtab;
  746.         curpersindex = 0;
  747.     }
  748.     
  749.     /* search the dictionary until the word is found or the words run out */
  750.     for (  ; curindex < hashsize;  curent++, curindex++)
  751.     {
  752.         if (curent->word != NULL
  753.           &&  REGEX (cmp_expr, curent->word, dummy) != NULL)
  754.         {
  755.         curindex++;
  756.         /* Everybody's gotta write a wierd expression once in a while! */
  757.         return curent++->word;
  758.         }
  759.     }
  760.     /* Try the personal dictionary too */
  761.     for (  ; curpersindex < pershsize;  curpersent++, curpersindex++)
  762.     {
  763.         if ((curpersent->flagfield & USED) != 0
  764.           &&  curpersent->word != NULL
  765.           &&  REGEX (cmp_expr, curpersent->word, dummy) != NULL)
  766.         {
  767.         curpersindex++;
  768.         /* Everybody's gotta write a wierd expression once in a while! */
  769.         return curpersent++->word;
  770.         }
  771.     }
  772.     return NULL;
  773.     }
  774. #endif /* REGEX_LOOKUP */
  775.